package com.spn.blog.api.service;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.spn.blog.api.vo.MessageVo;
import com.spn.blog.common.dao.pojo.ChatHistory;
import com.spn.blog.common.utils.JWTUtils;
import com.spn.blog.common.utils.RedisCacheUtil;
import com.spn.blog.service.login.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
// 朋友聊天wb连接的地址路径
@ServerEndpoint("/api/ws/talk/{token}")
@Slf4j
public class ChatFriendsEndpoint {

    /**
     * 这里必须加static和set工厂注入，因为ServerEndpoint是多列，只有初始化的时候彩有sysUserService，其它时候不会进行
     * 实例注入
     */
    private static SysUserService sysUserService;
    @DubboReference
    private void setSysUserService(SysUserService sysUserService){
        ChatFriendsEndpoint.sysUserService = sysUserService;
    }

    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;
    private String key;

    RedisCacheUtil redisCacheUtil = (RedisCacheUtil)RedisCacheUtil.getBean("redisCacheUtil");

    private static CopyOnWriteArraySet<ChatFriendsEndpoint> webSockets = new CopyOnWriteArraySet<>();
    // 用来存放每个客户端对应的WebSocket对象。若要实现服务端与单一客户端通信的话，可以使用Map来存放，其中Key可以为用户标识
    private static Map<String,Session> sessionPool = new ConcurrentHashMap<>();

    @OnOpen
    public void onOpen(Session session, @PathParam(value="token") String token) {
        String userId = (String)JWTUtils.checkToken(token).get(JWTUtils.userKey);
        this.key = userId;

        // 所有都是好友，只要有人连接进来，就把他加入到map中，userId 作为key
        this.session = session;
        webSockets.add(this);
        sessionPool.put(userId, session);
    }

    @OnClose
    public void onClose(Session session) {
        webSockets.remove(this);
        sessionPool.remove(key);

        log.info("【websocket消息】连接断开，总数为:" + webSockets.size());
    }


    /**
     【websocket消息】收到客户端消息:
     {
     "date":"2023-03-18 19:18:50",
     "text":{"text":"123123"},
     "mine":true,
     "name":"SPN",
     "img":"http://static.spnblog.top/95fe4fcb-e7f9-457d-b6d5-532e1a8ed6c1.png",
     "to":"1529768323050684418"
     "from": ""}
     **/
    @OnMessage
    public void onMessage(String message) {
        log.info("【websocket消息】收到客户端消息:" + message);
        MessageVo messageVo = JSON.parseObject(message, MessageVo.class);
        try {
            ChatHistory chatHistory = new ChatHistory();
            Map<String, String> map = (Map<String, String>) JSON.parse(messageVo.getText());
            String text = map.get("text");
            chatHistory.setText(text);
            chatHistory.setDate(messageVo.getDate());
            chatHistory.setToId(messageVo.getTo());
            chatHistory.setUserId(key);
            sysUserService.insertChatHistory(chatHistory);
            String to = messageVo.getTo();
            messageVo.setMine(false);
            messageVo.setFrom(key);
            ObjectMapper mapper = new ObjectMapper();
            String res = mapper.writeValueAsString(messageVo);
            sendTextMessage(to, res);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }

    // 此为广播消息
    public void sendAllMessage(String message) {
        log.info("【websocket消息】广播消息:" + message);
        for(ChatFriendsEndpoint webSocket : webSockets) {
            try {
                webSocket.session.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    // 此为单点消息 (发送文本)
    public void sendTextMessage(String key, String message) {
        Session session = sessionPool.get(key);
        if (session != null) {
            try {
                session.getBasicRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}
